package com.wind.cloud.common.lock;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

/**
 * redis乐观锁实现
 *
 * @author wind.
 */
public class SimpleDbLock implements ILock, InitializingBean {

    private static final String PRE = "lock:";

    @Autowired
    private DataSource dataSource;

    private JdbcTemplate jdbcTemplate;

    @Override
    public boolean tryLock(String key, String val) {
        return tryLock(key, val, 60000L);
    }

    @Override
    public boolean tryLock(String key, String val, long expireMills) {
        Assert.hasText(key, "key不能为空");
        Assert.hasText(val, "value不能为空");
        Assert.isTrue(expireMills > 0, "过期时间必需大于0");

        long currentTimeMillis = System.currentTimeMillis();
        String lockKey = lockKey(key);

        List<Map<String, Object>> list = jdbcTemplate.query("select lock_name, lock_value, lock_time  from t_sys_db_lock where lock_name = ?",
                new String[]{lockKey}, new ColumnMapRowMapper());
        if (list != null && !list.isEmpty()) {
            Map<String, Object> map = list.get(0);
            Long lockTime = (Long) map.get("lock_time");
            if (lockTime != null && currentTimeMillis > lockTime) {
                String lockValue = (String) map.get("lock_value");
                return jdbcTemplate.update("update t_sys_db_lock set lock_value = ?, lock_time = ? where lock_name = ? and lock_value = ?",
                        val, (currentTimeMillis + expireMills), lockKey, lockValue) == 1;
            }
            return false;
        } else {
            return jdbcTemplate.update("insert into t_sys_db_lock(lock_value, lock_time, lock_name) values (?, ?, ?)",
                    val, (currentTimeMillis + expireMills), lockKey) == 1;
        }
    }

    @Override
    public boolean lock(String key, String val, long timeoutMills) {
        return lock(key, val, 60000L, timeoutMills);
    }

    @Override
    public boolean lock(String key, String val, long expireMills, long timeoutMills) {
        long timeout = timeoutMills;
        while (timeout >= 0) {
            if (tryLock(key, val, expireMills)) {
                return true;
            }

            // 随机时间避免频率相同申请锁，减少竞争
            int sleepTime = ThreadLocalRandom.current().nextInt(500, 1000);
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                break;
            }
            timeout -= sleepTime;

        }
        return false;
    }

    @Override
    public boolean unLock(String key, String val) {
        Assert.hasText(key, "key不能为空");
        Assert.hasText(val, "value不能为空");

        String lockKey = lockKey(key);

        return jdbcTemplate.update("delete from t_sys_db_lock where lock_name = ? and lock_value = ?", lockKey, val) == 1;
    }

    private String lockKey(String key) {
        return PRE + key;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.setQueryTimeout(1000);
    }

}
